/**
 * @class   wosh::EncoderProtocol
 * @brief   
 *
 ****************************************************************************
 * @version 0.8.399 $Id: EncoderProtocol.h 2827 2010-08-02 11:01:48Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

#ifndef __WOSH_Core_EncoderProtocol_H__
 #define __WOSH_Core_EncoderProtocol_H__

 #include <woshDefs.h>
 #include <core/Object.h>
 #include <core/ObjectAllocator.h>
 #include <core/LogContext.h>
 #include <core/MethodsProvider.h>
 #include <core/PropertiesProvider.h>

namespace wosh {

//-----------------------------------------------------------------------------

 template <class T> class DataFieldCollector;

#undef WOSH_ENCODERBASE
#define WOSH_ENCODERBASE(CLASSNAME, OBJECT_TYPE) \
		WOSH_CLASSNAME(CLASSNAME)											\
		WOSH_CLASS_ABSTRACT(false)											\
		public: static const char* mappedObject() { return #OBJECT_TYPE; }	\
		public: virtual inline const char* getMappedObject() const { return mappedObject(); }


enum EncoderOptions {
	EncoderOption_COMPACT	= 0x0001, //
	EncoderOption_CRC32		= 0x0002 //

 };

class EncoderBase : public virtual IReflection
 {
	WOSH_CLASSNAME(wosh::EncoderBase)
	WOSH_CLASS_ABSTRACT(true)

	public:
		virtual int64 serializeTo( const ISerializable* object, char* buffer, int64 buffer_size, int encoder_options ) = 0;
		virtual int64 deserializeFrom( ISerializable* object, const char* buffer, int64 buffer_size ) = 0;

	public:
		virtual const char* getMappedObject() const = 0;

	public:
		static DataFieldCollector<EncoderBase>* getDataFields();

		static inline bool isOptionEnabled( int encoder_mask, EncoderOptions option ) {
			return ( (encoder_mask & static_cast<int>(option)) == static_cast<int>(option) );
		 }
		static inline bool isCompact( int encoder_mask ) {
			return isOptionEnabled(encoder_mask, EncoderOption_COMPACT);
		 }

	public:
		virtual ~EncoderBase() { }


}; // class def

//-----------------------------------------------------------------------------

#undef WOSH_ENCODER_BIND
#define WOSH_ENCODER_BIND(ENCODER_PROTOCOL, ENCODER_CLASS, OBJECT_CLASSNAME, VERSION, TMP ) \
	wosh::EncoderMapperT<ENCODER_PROTOCOL> static_encoder_mapper_##TMP(new ENCODER_CLASS(), OBJECT_CLASSNAME, VERSION); 

template <class ENCODER_PROTOCOL>
class EncoderMapperT
 {
	public:
		EncoderMapperT( EncoderBase* obj, const char* classname, double current_version = 0.0 ) {
			this->object = NULL;
			registerMySelf(obj, classname, current_version);
		}
		virtual ~EncoderMapperT() {
			unRegisterMySelf();
		 }

	private:
		void registerMySelf( EncoderBase* obj, const char* classname, double current_version = 0.0 ) {
			if ( obj == NULL || classname == NULL || strlen(classname) == 0 ) return;
			this->object = obj;
			ObjectTypeInfo info;
			info.name = classname;
			info.version = current_version;
			info.allocable = true;
			ENCODER_PROTOCOL::getEncoders().transactionBeginWrite();
			ENCODER_PROTOCOL::getEncoders().set( info, this->object);
			ENCODER_PROTOCOL::getEncoders().transactionEnd();
		 }

		void unRegisterMySelf() {
			if ( this->object == NULL ) return;
			ENCODER_PROTOCOL::getEncoders().transactionBeginWrite();
			ENCODER_PROTOCOL::getEncoders().eraseByValue(this->object, true);
			this->object = NULL;
			ENCODER_PROTOCOL::getEncoders().transactionEnd();
		 }

	protected:
		EncoderBase* object;
		friend class PersistenceManager;
		friend class EncoderProtocol;

}; // class def


//-----------------------------------------------------------------------------

class EncoderProtocol;
class ObjectTypeInfo;
class PersistenceDb;

class IEncoderProtocolListener
 {
	public:
		virtual void encoder_protocol_db_action( const std::string& db_name, EncoderProtocol* source ) = 0;

	public:
		virtual ~IEncoderProtocolListener() { }

};

#undef WOSH_ENCODERPROTOCOL
#define WOSH_ENCODERPROTOCOL(CLASSNAME) \
		WOSH_CLASSNAME(CLASSNAME)													\
		WOSH_CLASS_ABSTRACT(false)													\
		public: inline const wosh::MapT<wosh::ObjectTypeInfo, wosh::EncoderBase*>& getEncodersBase() const { return getEncoders(); }		\
		protected: inline wosh::MapT<wosh::ObjectTypeInfo, wosh::EncoderBase*>& getEncodersBase() { return getEncoders(); }				\
		protected: static inline wosh::EncoderBase* getEncoderFor_( const char* classname, double version = 0.0, bool recursive = true ) {	\
			return selectEncoder_(getEncoders(), classname, version, recursive );	\
		 }																			\
		protected: static wosh::MapT<wosh::ObjectTypeInfo, wosh::EncoderBase*>& getEncoders() {	\
			static wosh::MapT<wosh::ObjectTypeInfo, wosh::EncoderBase*> static_encoders;			\
			return static_encoders;													\
		 }																			\
		protected: friend class wosh::EncoderMapperT<CLASSNAME>;


 #define _EncoderProtocol_KEY_ProtocolPrefix		"ProtocolPrefix"
 #define _EncoderProtocol_KEY_ProtocolVersion		"ProtocolVersion"
 #define _EncoderProtocol_KEY_Priority				"Priority"

class EncoderProtocol : public Object,
						public virtual IPropertiesProviderListener,
						public MethodRunner
 {
	WOSH_CLASSNAME(wosh::EncoderProtocol)
	WOSH_CLASS_ABSTRACT(true)

	public:
		EncoderProtocol();
		virtual ~EncoderProtocol();


	public:
		virtual const char* getProtocol() const = 0;
		virtual double getProtocolVersion() const = 0;

	public:
		virtual PersistenceDb* createDb() const = 0;

		virtual WRESULT loadObjects( std::vector<ISerializable*>& objects, const std::string& parameters ) = 0;

		virtual WRESULT saveObject( const ISerializable* object, const std::string& parameters ) = 0;
		virtual WRESULT saveObjects( const std::vector<ISerializable*>& objects, const std::string& parameters ) = 0;

	public:
		virtual bool isSupported( const char* classname, double version = 0.0 ) const;

		virtual int64 serializeTo( const ISerializable* object, char* buffer, int64 buffer_size, int encoder_options );
		virtual int64 deserializeFrom( ISerializable* object, const char* buffer, int64 buffer_size );


//		virtual WRESULT serializeTo( const PersistenceDb* object ) = 0;
//		virtual WRESULT serializeTo( const PersistenceDb* object );

		virtual WRESULT init();

/** @name Getters
 * @{
 ******************************************************************************/
	public:
		virtual const MapT<ObjectTypeInfo, EncoderBase*>& getEncodersBase() const = 0;
	protected:
		virtual MapT<ObjectTypeInfo, EncoderBase*>& getEncodersBase() = 0;
	public:

		const PropertiesProvider& getProperties() const { return Properties; }
		PropertiesProvider& getProperties()				{ return Properties; }

		const MethodsProvider& getMethods() const		{ return this->Methods; }
		MethodsProvider& getMethods()					{ return this->Methods; }

		static DataFieldCollector<EncoderProtocol>* getDataFields();

//@}
/** @name Setters
 * @{
 ******************************************************************************/
	public:
		virtual WRESULT setListener( IEncoderProtocolListener* enc_l );

//@}
/** @name IPropertiesProviderListener interface
 * @{
 ******************************************************************************/
 	public:
		virtual bool readingProperty( Property*, const PropertiesProvider* ) { return true; }
		virtual bool updatingProperty( bool& do_update, const Variant& value_proposed, Property& property_current, const PropertiesProvider* source );
//@}
/** @name Internal methods
 * @{
 ******************************************************************************/
	protected:
//@}

		static EncoderBase* selectEncoder_( MapT<ObjectTypeInfo, EncoderBase*>& encoders, const char* classname, double version = 0.0, bool recursive = true );


	protected:
		mutable PropertiesProvider Properties;	///< my configuration
		mutable MethodsProvider Methods;		///< my methods
		mutable LogContext Log;					///< my logger

		bool initialized;

	private:
		IEncoderProtocolListener* listener;		///< class listening for events

		friend class PersistenceManager;		///< my big brother

}; // class def

}; // namespace wosh

#endif //__WOSH_Core_EncoderProtocol_H__
